<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> 
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
<title>Chat</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/mootools/1.2.4/mootools-yui-compressed.js"></script>
<script type="text/javascript">
var idCounter = 0;
function uniqID() {
    return idCounter++;
}

var WSJsonTransport = new Class({

    Implements: [Events, Options],

    options: {
        /*
         * onOpen: function()
         * onClose: function()
         */
        callbacks: {}
    },

    initialize: function(url, opt) {
        this.setOptions(opt);
        var inst = this;

        // attr
        this.status = 0;                                                // 0 not opened; 1 opened; -1 closed
        this.pendingCalls = [];
        this.outstandCalls = new Hash();                                // id -> callback
        this.transport = new WebSocket(url);

        this.transport.onopen = function() {
            $each(inst.pendingCalls, function(pC) {
                inst.transport.send(pC);
            });
            inst.status = 1;
            inst.fireEvent('open');
        };
        this.transport.onclose = function() {
            inst.status = -1;
            inst.fireEvent('close');
        },
        this.transport.onmessage = function(ev) {
            try {
                var res = JSON.decode(ev.data);
                if (res.jsonrpc != '2.0')
                    throw "bad server protocol";

                if (res.method) {                                       // a notify from server
                    var method = inst.options.callbacks[res.method];
                    if (!method)
                        throw "can't find method: {method}".substitute(res);
                    method.apply(inst, res.params);
                } else {                                                // response
                    var cb = inst.outstandCalls.get(res.id);
                    if (!cb)
                        throw "no such call id: {id}".substitute(res);
                    inst.outstandCalls.erase(res.id);
                    cb(res.id, res.result, res.error);                  // callback prototype
                }
            } catch (e) {
                alert(e);
                inst.transport.close();
                return;
            }
        };
    },
    close: function() {
        if (this.status == 1)
            this.transport.close();
    },
    call: function(method, params, cb) {
        var id = uniqID();
        this.outstandCalls.set(id, cb || $empty);
        var c = JSON.encode({'jsonrpc': '2.0', 'method': method, 
            'params': params, 
            'id': id
        });
        if (this.status == 1) {
            this.transport.send(c);
        }
        else if (this.status == 0) {
            this.pendingCalls.push(c);
        }
        else
            throw "transport closed";
        return id;
    }
});

var userName;
var chatRoom;

window.addEvent('domready', function() {
    chatContent = $("chatContent");
    function addMsg(msg) {
        chatContent.set("text", chatContent.get("text") + '\n' + msg);
    }

    function disableInputUsername() {
        $("username").setProperty("disabled", "true");
        $("inputUsername").setProperty("disabled", "true");
    }

    function enableInputUsername() {
        $("username").removeProperty("disabled");
        $("inputUsername").removeProperty("disabled");
    }

    function buildChatRoom() {
        if (chatRoom || !userName)
            return;

        var url = "ws://127.0.0.1:9876/ws/chat";

        chatRoom = new WSJsonTransport(url, {
            callbacks: {
                'msg': function(msg) {
                    addMsg(msg);
                }
            },
            onOpen: function() {
                chatRoom.call('login', [userName], function(callID, res, err) {
                    if (err) {
                        chatRoom.close();
                        chatRoom = null;
                        enableInputUsername();
                    }
                    else {
                        addMsg("You have entered the chatroom");
                        disableInputUsername();
                    }
                });
            },
            onClose: function() {
                addMsg("服务中断.");
                chatRoom = null;
                enableInputUsername();
            }
        });

    }
    $("inputUsername").addEvent("click", function(ev) {
        userName = $("username").getProperty("value");
        if (!userName) {
            alert("请输入用户名");
            return;
        }
        buildChatRoom();
    });
    $("chatForm").addEvent("submit", function(ev) {
        var msg = $("message").get("value");
        $("message").set("value", '');
        if (!chatRoom || !msg)
            return false;

        chatRoom.call("chat", [msg], function(callID, res, err) {
            if (err) {
                addMsg("<您的消息 '" + msg + "' 没有发送成功>");
            }
        });
        return false;
    });
});

</script>
</head>
<body>
    <div id="askUsername">
        请输入您的名字: <input type="text" id="username" name="username" /> <input type="button" id="inputUsername" name="inputUsername"  value="确定" />
    </div>
    <div>
        <textarea id="chatContent" rows="20" cols="80" readonly="true"></textarea>
    </div>
    <div>
        <form id="chatForm">
            <input type="text" id="message" size="80" /> <input type="submit" id="submit" value="发送" />
        </form>
    </div>
</body>
</html>
